package com.my.study.utils;

import com.alibaba.fastjson.JSON;
import com.my.study.aop.anntation.ImportField;
import com.my.study.enums.BusinessExceptionEnum;
import com.my.study.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 业界easypoi技术很成熟，此工具类个人开发练手
 */
@Slf4j
public class ExcelReadUtil {

    private static final int MAX_SHEET_NUM = 20;

    private static final int HEAD_ROW = 0;

    private static final int MAX_DATA_NUM = 10000;

    /**
     * 读取excel转成对应实体（实体注解的表头与excel表头一致）
     *
     * @param inputStream
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> List<T> readExcelData(InputStream inputStream, Class<T> tClass) {
        try {
            // 获取key
            Map<String, String> fieldMap = getFieldMap(tClass);
            // 生成workbook
            Workbook workbook = new XSSFWorkbook(inputStream);
            // excel文件校验
            checkExcel(workbook);
            // 读取数据
            int numberOfSheets = workbook.getNumberOfSheets();
            // 正常也会只读一个sheet页
            List<Map<String, String>> dataList = new ArrayList<>();
            for (int i = 0; i < numberOfSheets; i++) {
                Sheet sheetAt = workbook.getSheetAt(i);
                dataList.addAll(readCellValue(sheetAt, fieldMap));
            }
            return JSON.parseArray(JSON.toJSONString(dataList), tClass);
        } catch (Exception exception) {
            handlerException(exception);
            log.error("Excel file read failed." + exception);
            throw BusinessException.fail(BusinessExceptionEnum.FAIL.code, "Excel文件读取失败！");
        }
    }

    /**
     * 读取数据
     *
     * @param sheetAt
     * @param fieldMap
     * @return
     */
    public static List<Map<String, String>> readCellValue(Sheet sheetAt, Map<String, String> fieldMap) {
        // 数据集合
        List<Map<String, String>> dataList = new ArrayList<>();
        // 获取表头
        Row headRow = sheetAt.getRow(HEAD_ROW);
        // 获取最后一行
        int lastRowNum = sheetAt.getLastRowNum();
        // 数据循环
        for (int i = 1; i <= lastRowNum; i++) {
            Map<String, String> dataMap = new HashMap<>();
            Row row = sheetAt.getRow(i);
            for (int j = 0; j < lastRowNum; j++) {
                String headKey = headRow.getCell(j).getStringCellValue();
                dataMap.put(fieldMap.get(headKey.trim()), String.valueOf(getCellValue(row.getCell(j))));
            }
            dataList.add(dataMap);
        }
        return dataList;
    }

    /**
     * excel校验
     *
     * @param workbook
     */
    public static void checkExcel(Workbook workbook) {
        // 获取sheet个数
        int numberOfSheets = workbook.getNumberOfSheets();
        if (numberOfSheets > MAX_SHEET_NUM) {
            log.error("Excel file sheet page exceeds limit");
            throw BusinessException.fail(BusinessExceptionEnum.FAIL.code, "Excel文件sheet页超出限制！");
        }
        // 获取sheet页数据行数
        for (int i = 0; i < numberOfSheets; i++) {
            Sheet sheet = workbook.getSheetAt(i);
            int lastRowNum = sheet.getLastRowNum();
            if (lastRowNum > MAX_DATA_NUM) {
                log.error("Excel file data page exceeds limit");
                throw BusinessException.fail(BusinessExceptionEnum.FAIL.code, "Excel文件数据超出限制！");
            }
        }
    }

    /**
     * 获取类属性map
     *
     * @param tClass
     * @return
     */
    public static Map<String, String> getFieldMap(Class<?> tClass) {
        Map<String, String> map = new HashMap<>();
        // 获取属性集合
        Field[] declaredFields = tClass.getDeclaredFields();
        // 属性集合循环
        for (Field field : declaredFields) {
            // 获取注解
            ImportField annotation = field.getAnnotation(ImportField.class);
            if (Objects.isNull(annotation)) {
                continue;
            }
            map.put(annotation.name(), field.getName());
        }
        return map;
    }

    /**
     * 获取excel值
     *
     * @param cell
     * @return
     */
    private static Object getCellValue(Cell cell) {
        // 判断空值
        if (cell == null) {
            return "";
        }
        // 获取值
        Object result = "";
        CellType cellType = cell.getCellTypeEnum();
        switch (cellType.getCode()) {
            case 1: // 字符串类型
                result = cell.getStringCellValue();
                break;
            case 0:
                if (DateUtil.isCellDateFormatted(cell)) { // 日期类型
                    result = cell.getDateCellValue();
                } else { // 数字类型
                    result = cell.getNumericCellValue();
                }
                break;
            case 4: // 布尔类型
                result = cell.getBooleanCellValue();
                break;
            case 3: // 空白类型
                break;
        }
        return result;
    }

    /**
     * 异常处理
     *
     * @param exception
     */
    public static void handlerException(Exception exception) {
        if (exception instanceof BusinessException) {
            BusinessException businessException = (BusinessException) exception;
            throw BusinessException.fail(businessException.getCode(), businessException.getMsg());
        }
    }
}
